﻿/*************************************************************************************
 * 文 件 名:   HK_SmartCopy
 * 
 * 描    述:   对选中的物体进行智能复制，按Ctrl+Shift+D进行复制（只能且必须选中一个物体），
 *             第一次复制时会记录便宜的位移、旋转、缩放，之后再次按Ctrl+Shift+D可根据偏移量增量连续进行复制
 *             借鉴Maya中的复制功能）
 * 
 * 版    本：  V1.1
 * 创 建 者：  京产肠饭
 * 创建时间：  2021/8/26
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using UnityEditor;
using UnityEngine;

namespace HKTools
{
    [InitializeOnLoad]
    public class HK_SmartCopy
    {
        const int SmartCopyPriority = 0;

        //执行的步骤
        static int moveStep = 0;

        //选中的第一个物体 初始位置、旋转、缩放
        static Vector3 startPos;
        static Vector3 startEuler;
        static Vector3 startScale;

        static Vector3 firstForward;
        static Vector3 firstRight;
        static Vector3 firstUp;

        //通过复制出来的第一个物体 计算出来的 偏移位置、旋转、缩放
        static Vector3 offsetPos;

        static Vector3 offsetX;
        static Vector3 offsetY;
        static Vector3 offsetZ;

        static float offsetXVal;
        static float offsetYVal;
        static float offsetZVal;

        static Vector3 offsetEuler;

        static float xScaleIndex;
        static float yScaleIndex;
        static float zScaleIndex;

        //记录上一个复制的 位置和旋转
        static Vector3 lastPos;
        static Quaternion lastRotation;
        static Vector3 lastScale;

        static Transform parentTF = null;
        static GameObject lastSelectGo = null;
        static GameObject selectGo = null;

        [MenuItem("HKTools/SmartCopy(智能复制工具) %#D", validate = true, priority = SmartCopyPriority)]
        static bool CopyObjValidate()
        {
            return HK_ToolStates.IsActiveSmartCopy;
        }

        [MenuItem("HKTools/SmartCopy(智能复制工具) %#D", priority = SmartCopyPriority)]
        static void CopyObj()
        {
            // 判断是否只选中了一个物体 
            if (Selection.gameObjects.Length > 1)
            {
                Debug.Log("<color=red>错误:只能选择一个Hierarchy中的物体进行复制</color>");
                return;
            }
            if (Selection.gameObjects.Length == 0)
            {
                Debug.Log("<color=red>错误:未选中物体，无法执行复制操作</color>");
                return;
            }

            if (moveStep == 0)    // 第一步： 记录是否有 Parent ，Copy选中物体 ，记录第一个物体 三个轴向 和 初始位置旋转
            {
                //如果选中物体有父物体 ，则脱离父物体
                if (Selection.activeGameObject.transform.parent != null)
                {
                    parentTF = Selection.activeGameObject.transform.parent;
                    Selection.activeGameObject.transform.parent = null;
                }
                else
                {
                    parentTF = null;
                }

                lastSelectGo = Selection.activeGameObject;

                EditorApplication.ExecuteMenuItem("Edit/Duplicate");

                if (parentTF != null)
                    lastSelectGo.transform.SetParent(parentTF);

                //记录第一个物体的 三个轴向 & 开始的 位置和旋转
                firstForward = Selection.activeGameObject.transform.forward;
                firstRight = Selection.activeGameObject.transform.right;
                firstUp = Selection.activeGameObject.transform.up;

                startPos = Selection.activeGameObject.transform.position;
                startEuler = Selection.activeGameObject.transform.eulerAngles;

                startScale = Selection.activeGameObject.transform.localScale;

                selectGo = Selection.activeGameObject;

                if (parentTF != null)
                    selectGo.transform.SetParent(parentTF);

                moveStep++;
            }
            else if (moveStep == 1)   // 第二步 ： 判定 偏移向量 在各个轴向的分量，并复制
            {
                if (Selection.activeGameObject != selectGo)
                {
                    moveStep = 0;
                    CopyObj();
                    return;
                }

                //如果选中物体 位置与旋转 并没发生改变 ，则返回第一步
                if (Selection.activeGameObject.transform.position == startPos && Selection.activeGameObject.transform.eulerAngles == startEuler && Selection.activeGameObject.transform.localScale == startScale)
                {
                    moveStep = 0;
                    CopyObj();
                    return;
                }
                else
                {
                    parentTF = null;
                }

                //如果 选中物体 有父物体 ， 则脱离父物体
                if (Selection.activeGameObject.transform.parent != null)
                {
                    parentTF = Selection.activeGameObject.transform.parent;
                    Selection.activeGameObject.transform.parent = null;
                }

                //计算便宜的向量 和 每个轴向的分量
                offsetPos = Selection.activeGameObject.transform.position - startPos;

                offsetX = Vector3.Project(offsetPos, firstRight);
                offsetY = Vector3.Project(offsetPos, firstUp);
                offsetZ = Vector3.Project(offsetPos, firstForward);

                if (Vector3.Dot(firstRight.normalized, offsetX.normalized) > 0)
                    offsetXVal = offsetX.magnitude;
                else
                    offsetXVal = -offsetX.magnitude;
                if (Vector3.Dot(firstForward.normalized, offsetZ.normalized) < 0)
                    offsetZVal = -offsetZ.magnitude;
                else
                    offsetZVal = offsetZ.magnitude;
                if (Vector3.Dot(firstUp.normalized, offsetY.normalized) < 0)
                    offsetYVal = -offsetY.magnitude;
                else
                    offsetYVal = offsetY.magnitude;

                offsetEuler = Selection.activeGameObject.transform.eulerAngles - startEuler;

                lastSelectGo = Selection.activeGameObject;

                EditorApplication.ExecuteMenuItem("Edit/Duplicate");

                if (parentTF != null)
                    lastSelectGo.transform.SetParent(parentTF);

                Selection.activeGameObject.transform.Translate(offsetXVal, offsetYVal, offsetZVal, Space.Self);
                Selection.activeGameObject.transform.eulerAngles = Selection.activeGameObject.transform.eulerAngles + offsetEuler;

                lastPos = Selection.activeGameObject.transform.position;
                lastRotation = Selection.activeGameObject.transform.rotation;

                if (parentTF != null)
                    Selection.activeGameObject.transform.parent = parentTF;

                xScaleIndex = Selection.activeGameObject.transform.localScale.x / startScale.x;
                yScaleIndex = Selection.activeGameObject.transform.localScale.y / startScale.y;
                zScaleIndex = Selection.activeGameObject.transform.localScale.z / startScale.z;

                float x = Selection.activeGameObject.transform.localScale.x * xScaleIndex;

                float y = Selection.activeGameObject.transform.localScale.y * yScaleIndex;

                float z = Selection.activeGameObject.transform.localScale.z * zScaleIndex;

                Selection.activeGameObject.transform.localScale = new Vector3(x, y, z);

                lastScale = Selection.activeGameObject.transform.localScale;

                selectGo = Selection.activeGameObject;

                if (parentTF != null)
                    selectGo.transform.SetParent(parentTF);

                moveStep++;
            }
            else if (moveStep == 2 && Selection.gameObjects.Length == 1)   // 第三步：就是一直复制呗
            {
                if (Selection.activeGameObject != selectGo)
                {
                    moveStep = 0;
                    CopyObj();
                    return;
                }

                if (Vector3.Distance(Selection.activeGameObject.transform.position, lastPos) > 0.01f
                    || Quaternion.Angle(Selection.activeGameObject.transform.rotation, lastRotation) > 0.1f
                    || Vector3.Distance(Selection.activeGameObject.transform.localScale, lastScale) > 0.01f)
                {
                    Debug.Log("重新复制");
                    moveStep = 0;
                    CopyObj();
                    return;
                }
                else
                {
                    parentTF = null;
                }

                //如果 选中物体 有父物体 ， 则脱离父物体
                if (Selection.activeGameObject.transform.parent != null)
                {
                    parentTF = Selection.activeGameObject.transform.parent;
                    Selection.activeGameObject.transform.parent = null;
                }

                lastSelectGo = Selection.activeGameObject;

                EditorApplication.ExecuteMenuItem("Edit/Duplicate");

                if (parentTF != null)
                    lastSelectGo.transform.SetParent(parentTF);

                Selection.activeGameObject.transform.Translate(offsetXVal, offsetYVal, offsetZVal, Space.Self);
                Selection.activeGameObject.transform.eulerAngles = Selection.activeGameObject.transform.eulerAngles + offsetEuler;

                lastPos = Selection.activeGameObject.transform.position;
                lastRotation = Selection.activeGameObject.transform.rotation;

                if (parentTF != null)
                    Selection.activeGameObject.transform.parent = parentTF;

                float x = Selection.activeGameObject.transform.localScale.x * xScaleIndex;

                float y = Selection.activeGameObject.transform.localScale.y * yScaleIndex;

                float z = Selection.activeGameObject.transform.localScale.z * zScaleIndex;

                Selection.activeGameObject.transform.localScale = new Vector3(x, y, z);

                lastScale = Selection.activeGameObject.transform.localScale;

                selectGo = Selection.activeGameObject;

                if (parentTF != null)
                    selectGo.transform.SetParent(parentTF);
            }
        }
    }
}